#!/usr/bin/env bash
set -o pipefail -o errexit -o nounset

# Copyright 2024 Google Inc. All rights reserved.

# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at

#     http://www.apache.org/licenses/LICENSE-2.0

# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

source knife.d/update_java_versions.sh
source knife.d/update_java_archives.sh

if [ $(uname) == "Darwin" ]; then
    echo "WARNING: You are on a macos, you need to run 'brew install coreutils gnu-sed' to install required packages."
    echo ""
    export PATH="/opt/homebrew/opt/coreutils/libexec/gnubin:$PATH"
    export PATH="/opt/homebrew/opt/gnu-sed/libexec/gnubin:$PATH"
fi

function cmd_lock_all() {
    cmd_lock_snapshots
    cmd_lock_non_snapshots
}

function cmd_lock_snapshots() {
    echo "🚧 Querying for snapshot repos"
    echo ""
    local repos=$(grep -l snapshot.debian.org ./private/repos/deb/*.yaml | xargs -L 1 basename | cut -d. -f 1)
    _cmd_lock "$repos"
    update_java_versions_debian12
}

function cmd_lock_non_snapshots() {
    echo "🚧 Querying for non_snapshot repos"
    echo ""
    local repos=$(grep -lL snapshot.debian.org ./private/repos/deb/*.yaml | xargs -L 1 basename | cut -d. -f 1)
    _cmd_lock "$repos"
    update_java_versions_debian13
}

function _cmd_lock() {
    local repos="$1"
    for repo in $repos; do
      for i in $(seq 10); do
        echo "🔑 Locking $repo (attempt ${i})"
        bazel run "@${repo}//:lock" && break || sleep 20;
        if [[ $i -eq 10 ]]; then
          echo ""
          echo "Failed to lock $repo after 10 attempts" >&2
          exit 1
        fi
      done
    done
}

function find_latest_snapshot() {
    local type="$1"
    # If it's the first of the month, look at the last month, otherwise this -1 day has no effect since searches
    # occur at the "month" level. This is an intentional buffer added to get the snapshots fully hydrated. We
    # intentionally don't include complicated logic for the case where it's after the 1st and no snapshots are
    # availalbe for the month (it's extremely unlikely for our updater to run into this situation unless the
    # snapshot serving infrastructure is acting up).
    local current="$(date -d '-1 day' +%Y-%m-%d)"
    local tmp=$(mktemp)
    local q=$(date -d "$current" +"year=%Y&month=%m")
    if curl -fs "https://snapshot.debian.org/archive/debian/?$q" | grep -ohE "([0-9]+T[0-9]+Z)" > $tmp; then
      # same logic as above, find the newest snapshot that isn't "today"
      today=$(date +"%Y%m%dT")
      cat $tmp | grep -v $today | tail -n1
    fi
}

function cmd_update_snapshots() {
    echo "🧐 Looking for updates... "
    latest=$(find_latest_snapshot "debian")
    latest_security=$(find_latest_snapshot "debian-security")
    if [[ -z "$latest" || -z "$latest_security" ]]; then
        echo ""
        echo "could not find any snapshots for debian or debian-security"
        exit 1
    fi
    echo ""
    echo "🎯 Found snapshots"
    echo "   debian: $latest"
    echo "   security: $latest_security"
    echo ""

    # if tty ask for approval
    if [ -t 1 ]; then
        read -p "Do you want to continue? (y/n) " -n 1 -r
        sleep 0.5
        echo $'\n'
        if [[ ! "$REPLY" =~ ^[Yy]$ ]]; then
            echo "Aborting..."
            exit 0
        fi
    fi

    for mpath in "./private/repos/deb/"*.yaml; do
        if ! grep -q "snapshot.debian.org" "$mpath"; then
            echo "ignoring non-snapshot repo $mpath"
            continue
        fi

        current=$(grep -oE "debian/([0-9]+T[0-9]+Z)" $mpath | cut -d/ -f2 | head -n1)
        current_security=$(grep -oE "debian-security/([0-9]+T[0-9]+Z)" $mpath | cut -d/ -f2 | head -n1)

        if [[ "$current" == "$latest" && "$current_security" == "$latest_security" ]]; then
            echo "🎖️ $mpath is up to date."
            continue
        fi
        echo "🗞️ $mpath"
        if [[ "$current" != "$latest" ]]; then
            sed -i -E "s/(debian\/)([0-9]+T[0-9]+Z)/\1$latest/" "$mpath"
            echo "   debian: $current -> $latest"
        fi
        if [[ "$current_security" != "$latest_security" ]]; then
            sed -i -E "s/(debian-security\/)([0-9]+T[0-9]+Z)/\1$latest_security/" "$mpath"
            echo "   debian-security: $current_security -> $latest_security"
        fi
        echo ""
    done
    echo ""
    echo "👌 Done..."
}

# write DISTROLESS_DIFF to GITHUB_ENV if changes are made
function cmd_github_update_snapshots() {
    local tmp=$(mktemp -d)
    jq -nr 'inputs.packages[] | .key + " " + .sha256' ./private/repos/deb/*.lock.json | sort > "$tmp/old.hashes"
    cmd_update_snapshots
    cmd_lock_snapshots
    jq -nr 'inputs.packages[] | .key + " " + .sha256' ./private/repos/deb/*.lock.json | sort > "$tmp/new.hashes"
    diff "$tmp/old.hashes" "$tmp/new.hashes" | tee "$tmp/diff" || printf "DISTROLESS_DIFF<<EOF\n$(<$tmp/diff)\nEOF" >> "$GITHUB_ENV"
}

function cmd_lint () {
    echo "🧹 Linting"
    echo ""
    if ! which buildifier > /dev/null; then
        echo "🧱 No buildifier executable was found."
        echo " Did you follow the ./CONTRIBUTING.md ?"
        exit 1
    fi
    buildifier -mode=fix $(find . -name 'BUILD*' -o -name 'WORKSPACE*' -o -name '*.bzl' -type f)
}

# deprecate with debian12
function cmd_update_java_archives () {
    generate_java_archives 21 > private/repos/java_temurin/java_21.MODULE.bazel
    update_test_versions_java21_debian12
}

function cmd_update_node_archives () {
	if ! which jq > /dev/null; then
		echo "🧱 No jq executable was found"
		exit 1
    fi
	if ! which curl > /dev/null; then
		echo "🧱 No curl executable was found"
		exit 1
	fi
	if ! which node > /dev/null; then
		echo "🧱 No node executable was found"
		exit 1
	fi

    versions=()
    for major in 20 22 24; do
        latest_version=$(curl -sSL https://nodejs.org/dist/index.json | jq -r --arg major "v$major" '
            map(select(.version | startswith($major)))
            | sort_by(.date) | reverse | .[0].version
        ')
        latest_nov=$(echo "$latest_version" | sed 's/^v//')
        versions+=("$latest_nov")
    done

    joined_versions=$(IFS=, ; echo "${versions[*]}")
    node knife.d/update_node_archives.js "$joined_versions"
}


function cmd_test () {
    echo "🧪 Testing"
    echo ""

    local arch=$(uname -m)
    if [ ${arch} == "x86_64" ]; then
      arch="amd64"
    fi

    echo "💡 only including image tests for $arch"
    echo ""

    arch_specific_targets=$(bazel query "attr(\"tags\", "$arch", \"//...\")")

    # Run all tests tagged with "amd64"
    bazel test --test_timeout=900 //... $arch_specific_targets
}

function cmd_deb_versions () {
    echo "🔧 Printing .deb Versions (bookworm) from private/repos/deb/bookworm*.lock.json"
    echo ""

    jq -n '[inputs.packages[]] | group_by(.arch) | map({(.[0].arch): map({package: .name, version: .version})})' private/repos/deb/bookworm*.lock.json
}

case "${1:-"~~nocmd"}" in
lock)
    cmd_lock_all
    ;;
update-snapshots)
    cmd_update_snapshots
    ;;
update-non-snapshots)
    cmd_lock_non_snapshots
    ;;
lint)
    cmd_lint
    ;;
github-update-snapshots)
    cmd_github_update_snapshots
    ;;
test)
    cmd_test
    ;;
update-java-archives)
    cmd_update_java_archives
    ;;
deb-versions)
    cmd_deb_versions
    ;;
update-node-archives)
	cmd_update_node_archives
	;;
~~nocmd) # no command provided
    echo "provide a command: lock, update-snapshots, github-update-snapshots, update-non-snapshots, update-java-archives, test, deb-versions, update-node-archives"
    exit 1
    ;;
*) # unknown command
    echo "unknown command $1"
    exit 1
    ;;
esac
